package com.kehutong.admin.util.service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.coraframework.authz.Subject;
import org.coraframework.cache.Cache;
import org.coraframework.cache.support.FIFOCache;
import org.coraframework.cache.support.LRUCache;
import org.coraframework.converter.util.TypeCache;
import org.coraframework.inject.Inject;
import org.coraframework.inject.Singleton;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.http.result.Result;
import org.coraframework.orm.BaseEntity;
import org.coraframework.orm.Entity;
import org.coraframework.orm.EntityCache;
import org.coraframework.orm.Pool;
import org.coraframework.orm.bytecode.CodeFactory;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.Page;
import org.coraframework.util.Objects;
import org.coraframework.util.time.TimeUtil;

import com.kehutong.admin.entity.MCache;

/**
 * @author liuzhen (liuxing521a@163.com)
 * @createTime 2020年6月19日下午7:05:57
 */
@Singleton
public class CacheService {
	
	private static final Logger logger = LoggerFactory.getLogger(CacheService.class);

	/** 默认生命 周期3天*/
	private static final long DEFAULT_CACHE_LIFE = 3L * TimeUtil.MILLIS_PER_DAY;
	/** 默认占用内存 10M*/
	private static final long DEFAULT_CACHE_MAX_SIZE = 10*1024*1024L;

    @Inject
    private CodeFactory codeFactory;
    @Inject
    private JdbcSession jdbcSession;

    public Object list(JSONObject jsonObject) {
    	final Page<MCache> cacheList = jdbcSession.findPage(MCache.class)
    			.like(jsonObject, "")
    			.page(1, 10000)
    			.exe();
    	cacheList.getList().forEach(ch->{
			EntityCache<?> ecache = codeFactory.getTemplate(ch.getClazz());
			if (ecache != null) {
				ch.setCache(ecache.getCache());
			}
    	});
    	
        return Result.success(cacheList);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Object update(MCache cache) throws ClassNotFoundException {
    	jdbcSession.updateById(cache);
    	
    	EntityCache<?> entityCache = codeFactory.getTemplate(cache.getClazz());
    	if (!cache.isOpened()) {
    		entityCache.setCache(null);
    		return Result.success();
    	}
    	
		Cache ch;
    	if (entityCache.getCache() == null) {
    		ch = createCache(cache);
    		entityCache.setCache(ch);
    	} else {
    		ch = entityCache.getCache();
    		ch.setName(cache.getName());
    		ch.setMaxCacheSize(cache.getMaxCacheSize());
    		ch.setMaxLifetime(cache.getMaxLifetime());
    	}
    	
        return Result.success();
    }

    public Object synCache() throws ClassNotFoundException {
    	final TypeCache<EntityCache<? extends BaseEntity>> typCache = codeFactory.getTemplatCache();
    	
    	List<MCache> cacheList = jdbcSession.findArray(MCache.class).exe();
    	removeCache(typCache, cacheList);
    	List<MCache> newList = addCache(typCache);
    	jdbcSession.insert(newList);
    	
        return Result.success();
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void init() {
    	try {
			final List<MCache> cacheList = jdbcSession.findArray(MCache.class)
					.eq("opened", true)
					.exe();
			
			for (MCache mcache : cacheList) {
				EntityCache<?> entityCache = codeFactory.getTemplate(mcache.getClazz());
				if (Objects.isNull(entityCache)) {
					jdbcSession.deleteById(mcache);
					continue;
				}
				Cache ch = createCache(mcache);
				entityCache.setCache(ch);
			}
		} catch (Exception e) {
			logger.error("", e);
		}
    }
    
    @SuppressWarnings("rawtypes")
	private void removeCache(TypeCache<EntityCache<? extends BaseEntity>> typCache, List<MCache> cacheList) throws ClassNotFoundException {
    	Iterator<MCache> it = cacheList.iterator();
    	while (it.hasNext()) {
    		MCache ch = it.next();
    		EntityCache cache = typCache.get((Class)ch.getClazz());
    		if (Objects.isNull(cache)) {
    			it.remove();
    			jdbcSession.deleteById(ch);
    		}
    	}
    }
    
    private List<MCache> addCache(TypeCache<EntityCache<? extends BaseEntity>> typCache) {
    	final List<MCache> newList = new ArrayList<>(typCache.size());
    	typCache.forEachValue(cache->{
    		final String id = Subject.encrypt(cache.getClazz().getName());
    		MCache old = Pool.get(MCache.class, id);
    		if (Objects.isNull(old)) {
    			MCache addCache = Pool.newInstance(MCache.class, id);
    			addCache.setName(cache.getClazz().getName());
    			addCache.setMaxCacheSize(DEFAULT_CACHE_MAX_SIZE);
    			addCache.setMaxLifetime(DEFAULT_CACHE_LIFE);
    			newList.add(addCache);
    		}
    	});
    	
    	return newList;
    }

    private Cache<String, ? extends Entity> createCache(MCache cache) {
    	switch (cache.getCacheType()) {
		case MCache.CACHE_TYPE_FIFO: return new FIFOCache<>(cache.getName(), cache.getMaxCacheSize(), cache.getMaxLifetime());
		case MCache.CACHE_TYPE_LRU: return new LRUCache<String, Entity>(cache.getName(), cache.getMaxCacheSize(), cache.getMaxLifetime());
		case MCache.CACHE_TYPE_MAP: return new LRUCache<String, Entity>(cache.getName(), cache.getMaxCacheSize(), cache.getMaxLifetime());
		default: throw new RuntimeException("unsupport type：" + cache.getCacheType());
		}
    }
}
